5329. Вечерка

 

Сколькими способами среди n ЛКШат можно выбрать k таких, которым достанется кефир? Ответ выведите по модулю 9929.

 

Вход. Целые числа n и k (0 ≤ kn ≤ 500).

 

Выход. Выведите искомое количество способов по модулю 9929.

 

Пример входа

Пример выхода

6 3

20

 

 

РЕШЕНИЕ

комбинаторика – биномиальный коэффициент

 

Анализ алгоритма

Ответом к задаче будет значение . Поскольку следует найти биномиальный коэффициент по модулю, постараемся при вычислениях избежать деления. Для этого воспользуемся соотношением  =  + ,  = 1.

 

Задачу можно решить также по формуле

 =  = ,

заменив деление умножением на обратное число по модулю 9929. Отметим, что число 9929 простое. То есть n / i = n * (i-1) mod 9929.

По теореме Ферма i9928 mod 9929 = 1 или (i * i9927) mod 9929 = 1, откуда обратным к i по модулю 9929 будет i9927 mod 9929. Таким образом

(i-1) mod 9929 = i9927 mod 9929

 

Реализация алгоритма

Объявим константы и массив cnk, для которого cnk[n][k] = .

 

#define MAX 510

#define MOD 9929

int cnk[MAX][MAX];

 

Функция c вычисляет значение биномиального коэффициента .

 

int c(int n, int k)

{

  if (cnk[n][k] > 0) return cnk[n][k];

  if (n - k < k) return c(n,n-k);

  if (!k) return cnk[n][k] = 1;

  return cnk[n][k] = (c(n-1,k) + c(n-1,k-1)) % MOD;

}

 

Основная часть программы. Инициализируем массив cnk нулями. Читаем входные данные.

 

memset(cnk,0,sizeof(cnk));

scanf("%d %d",&n,&k);

 

Вычисляем и выводим ответ.

 

printf("%d\n",c(n,k));

 

Реализация алгоритма циклическая

Объявим константы и массив cnk, для которого cnk[n][k] = .

 

#define MAX 502

#define MOD 9929

int cnk[MAX][MAX];

 

Функция FillCnk заполняет массив cnk биномиальными коэффициентами.

 

void FillCnk(void)

{

  int n, k;

  memset(cnk,0,sizeof(cnk));

 

Инициализация cnk[n][0] =  = 1.

 

  for(n = 0; n < MAX; n++) cnk[n][0] = 1;

 

Проводим вычисления, воспользовавшись формулой  =  + . Вычисления проводим по модулю MOD.

 

  for(n = 1; n < MAX; n++)

  for(k = 1; k <= MAX; k++)

    cnk[n][k] = (cnk[n-1][k] + cnk[n-1][k-1]) % MOD;

}

 

Основная часть программы. Заполняем массив биномиальных коэффициентов.

 

FillCnk();

 

Читаем входные данные. Вычисляем и выводим ответ.

 

scanf("%d %d",&n,&k);

printf("%d\n",cnk[n][k]);

 

Реализация алгоритма – обратное по модулю

Функция pow вычисляет xn mod p.

 

int pow(int x, int n, int p)

{

  if (n == 0) return 1;

  if (n % 2 == 0) return pow((x * x) % p, n / 2, p);

  return (x * pow(x, n - 1, p)) % p;

}

 

Функция inverse вычисляет число, обратное x, по модулю p: x-1 mod p (p простое).

 

int inverse(int x, int p)

{

  return pow(x, p - 2, p);

}

 

Функция Cnk вычисляет значение биномиального коэффициента  по модулю p. Для этого выражение

res = res * (ni + 1) / i

перепишем в виде

res = (res * (ni + 1) * (i-1 mod p)) mod p

 

int Cnk(int n, int k, int p)

{

  int res = 1;

  if (k > n - k) k = n - k;

  for (int i = 1; i <= k; i++)

    res = ((res * (n - i + 1)) % p * inverse(i, p)) % p;

  return res;

}

 

Основная часть программы. Читаем входные данные.

 

scanf("%d %d", &n, &k);

 

Вычисляем и выводим ответ.

 

res = Cnk(n, k, 9929);

printf("%d\n", res);

 

Java реализация

 

import java.util.*;

 

public class Main

{

  static int dp[][];

  static int MOD = 9929;

 

  static int Cnk(int n, int k)

  {

    if (n == k) return 1;

    if (k == 0) return 1;

    if (dp[n][k] != -1) return dp[n][k];

    return dp[n][k] = (Cnk(n - 1, k - 1) + Cnk(n - 1, k)) % MOD;

  }

  public static void main(String[] args)

  {

    Scanner con = new Scanner(System.in);

    int n = con.nextInt();

    int k = con.nextInt();

    dp = new int[n+1][k+1];

    for(int i = 0; i <= n; i++)

      Arrays.fill(dp[i],-1);

   

    int res = Cnk(n,k);

    System.out.println(res);

    con.close();

  }

}

 

Java реализация – итерационная, длинная арифметика

 

import java.util.*;

import java.math.*;

 

public class Main

{

  static BigInteger Cnk(int n, int k)

  {

    BigInteger res = BigInteger.ONE;

    BigInteger MOD = BigInteger.valueOf(9929);

    BigInteger p = BigInteger.valueOf(n);

    BigInteger q = BigInteger.valueOf(1);

    for (int i = 0; i < k; i++)

    {

      res = res.multiply(p).multiply(q.modInverse(MOD)).mod(MOD);

      p = p.subtract(BigInteger.ONE);

      q = q.add(BigInteger.ONE);

    }

    return res;

  }

 

  public static void main(String[] args)

  {

    Scanner con = new Scanner(System.in);

    int n = con.nextInt();

    int k = con.nextInt();

         

    BigInteger res = Cnk(n,k);

    System.out.println(res);

    con.close();

  }

}

 

Python реализация

Функция Cnk вычисляет значение биномиального коэффициента  и запоминает его значение в ячейке dp[n][k].

 

def Cnk(n, k, dp):

  if n == k: return 1

  if k == 0: return 1

  if dp[n][k] != -1: return dp[n][k]

  dp[n][k] = (Cnk(n - 1, k - 1, dp) + Cnk(n - 1, k, dp)) % 9929

  return dp[n][k]

 

Основная часть программы. Читаем входные данные.

 

n, k = map(int, input().split())

 

Инициализируем двумерный список для запоминания значений:

dp[x][y] =

 

dp = [[-1] * 501 for _ in range(501)]

 

Вычисляем и выводим ответ.

 

print(Cnk(n, k, dp))